// CODE 0, 使用free rtos软件定时器
// CODE 1, 使用AsyncTimer库


// 使用软件定时器，



// 硬件定时器的精度很高，可以到ns，硬件定时器会触发中断，数量只有4个
// 软件定时器，精度一般为ms，会触发回调函数
// aa


#include "main.h"

#define CODE 1


#if 0 == CODE

  //软件定时器，free rtos，软件计数精准，分毫不差
  //记数单位最小是1ms，再小做不到
  
  
  //定时器把手
  TimerHandle_t lock_h, check_h;



  // 锁车定时器的回调函数
  void lock_car_callback(TimerHandle_t timer){
  
    //打印已计数的时间？
    Serial.print(xTaskGetTickCount());
    Serial.println("时间回调：车已锁好");
  }


  //检查传感器回调函数？
  void check_callback(TimerHandle_t timer){

    Serial.print(xTaskGetTickCount());
    Serial.println("--所有传感器正常");
  }


  void car_key(void *p){
    // byte lock_pin = 4;
    pinMode(LOCK_PIN, INPUT_PULLUP);

    while (1){
      if(!digitalRead(LOCK_PIN)){
        
        
        // Serial.printf("%d\n", xTaskGetTickCount());
        
      
        // 如果该函数成功启动了定时器，则会返回 pdPASS。
        // 实际定时器把手是2000tick，此处是等待3000？
        // 此处3000有什么用，会在3000tick后关闭定时器么？
        // 此处不是创建定时器，所以频繁按键，每次都是重新开始计数
        if(xTimerStart(lock_h, 3000) == pdPASS){
          Serial.print(xTaskGetTickCount());
          Serial.println("正在锁车……");

        }else{
          Serial.println("无法锁车");
        }

        // 按键消抖
        vTaskDelay(100);
      }
    }
    
  }



  void setup() {

    // 关闭看门狗，否则程序一直重启
    disableCore0WDT();

    Serial.begin(115200);

    // 内存单位为byte，为什么内存要1024的倍数
    // 内存设置值推荐为使用值的2倍
    // 这个任务好像没用
    xTaskCreate(car_key, "锁车任务", 1024*1, NULL, 1, NULL);


    // 创建定时器把手，单次记时
    // 参数依次为 注释，时间，pdFALSE，单次，不重复，(void *)0是id，必须是指针，回调函数
    // 定时器3要素，定时器把手，回调函数，开启定时器
    // 可以创建无限个软定时器
    lock_h = xTimerCreate("lock_car", 2000, pdFALSE, (void *)0, lock_car_callback);

    
    // pdTRUE，定时器重复记时
    check_h = xTimerCreate("sensor_check", 700, pdTRUE, (void *)1, check_callback);

    // 开启检查传感器定时器
    // portMAX_DELAY是到最大计数上限，此处会阻塞
    // 定时器后面的时间是什么意思？
    xTimerStart(check_h, 20000);

  }

  void loop() {
    // put your main code here, to run repeatedly:
  }



#elif 1 == CODE
  
  //软件定时器，使用外部库AsyncTimer
  

  
  
  int led_state = HIGH;
  int t = 1;
  
  // 什么情况下要加域？
  RBD::Button button(BUTT_PIN, INPUT_PULLUP);

  AsyncTimer timer;
  
  int task_id = 0;
  
  
  // 回调函数，无返回值，无参数
  void change_led_state(){
    led_state = !led_state;
    digitalWrite(LED_PIN, led_state);
  }
  
  
  void setup() {

    pinMode(LED_PIN, OUTPUT);
    digitalWrite(LED_PIN, HIGH);
    button.setDebounceTimeout(20);
    
    // 创建周期性任务
    // 参数是回调函数，计时时间
    // 返回值是任务id
    task_id = timer.setInterval(change_led_state, t*1000);
    
    // 创建单次定时器
    // timer.setTimeout(change_led_state, t*1000);
    
    // 停止定时任务
    // timer.cancel(task_id);
    
    // 停止所有任务，false，取消所有单次定时任务，true，取消所有，周期性和一次性
    // timer.cancelAll(true);
    

  }

  void loop() {

    // 必须在loop中加上定时器handle
    // 此handle包含计时，时间重置，调用回调等
    // 在执行下面程序时，即使计时到了，也要等到执行handle时，才能调用回调，
    // 所以精度与loop函数复杂度有关，不高
    timer.handle();
    
    if(button.onPressed()){
      t = (t == 1?3:1);
      
      // 修改定时器的时间
      timer.changeDelay(task_id, t*1000);
    }
    
  }
  
#elif 2 == CODE

  

  // 使用硬件定时器


  // 创建定时器把手
  hw_timer_t *t = NULL;
  hw_timer_t *t_once = NULL;




  // 创建定时器中断处理函数
  void inte_timer(){

    // 切换引脚电平
    digitalWrite(LED, !digitalRead(LED));

  }

  void inte_timer_once(){
    digitalWrite(LED_ONCE, !digitalRead(LED_ONCE));
  }

  void setup() {
    // put your setup code here, to run once:
    pinMode(LED, OUTPUT);
    pinMode(LED_ONCE, OUTPUT);

    // 初始化定时器，创建定时器把手
      // 定时器编号，0-3，一共4个
      // 80是分频系数，什么意思？
      // true向上计数，false向下
    t = timerBegin(0, 80, true);
    t_once = timerBegin(1, 80, true);

    // 关联定时器与中断处理函数
      // 中断处理函数的参数可选参数输入，本例无参数
      // true边沿触发，false电平触发
      // 上升沿触发还是下降沿？
    timerAttachInterrupt(t, inte_timer, true);
    timerAttachInterrupt(t_once, inte_timer_once, true);

    // 设置定时器计数值
      // 1000*1000，单位是um
      // true自动充载，false单次触发
    timerAlarmWrite(t, 1000*1000, true);
    timerAlarmWrite(t_once, 3000*1000, false);

    // 启动定时器
    timerAlarmEnable(t);
    timerAlarmEnable(t_once);

    // 这个定时器设置，比stm32复杂，那边图形界面设置好，直接启动就行
    
    //获取定时器是否自动重载
    //timerGetAutoReload(t);



  }

  void loop() {
    // put your main code here, to run repeatedly:
  }


#elif 3 == CODE

  //软件定时器，使用自带库Ticker
  
  //定时器把手
  Ticker timer;
  Ticker timer_once;

  // put function declarations here:
  void toggle(int pin){
    digitalWrite(pin, !digitalRead(pin));
  }

  void setup() {
    // put your setup code here, to run once:
    pinMode(LED, OUTPUT);
    pinMode(LED_ONCE, OUTPUT);

    // 定时器初始化
    timer.attach(0.5, toggle, LED);
    timer_once.attach(3, toggle, LED_ONCE);
  }

  void loop() {
    // put your main code here, to run repeatedly:
  }

#endif